package com.example.common.util;

import cn.hutool.core.collection.CollUtil;
import com.example.common.util.util_entity.core.PageCondition;
import com.example.common.util.util_entity.core.PageResult;
import com.example.common.util.util_entity.core.Result;
import com.example.common.util.util_entity.core.TableDataInfo;
import org.springframework.beans.BeanUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

/**
 * 分页工具类
 *
 * @author addzero
 * @since 2022/5/4 11:37 AM
 */
@SuppressWarnings("unused")
public class PageUtils {

    /**
     * 分页api割接得到所有列表
     *
     * @param limitSize 限制大小
     * @param inBO      入参业务BO
     * @param convert   转换
     * @param api       api
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/06
     */
    public static <In, ApiOut, ApiIn extends PageCondition> List<ApiOut> getAllListFromTab(final int limitSize,
                                                                                           In inBO,
                                                                                           Function<In, ApiIn> convert,
                                                                                           Function<ApiIn, Result<TableDataInfo<ApiOut>>> api) {
        final ApiIn apiIn = convert.apply(inBO);
        List<ApiOut> ret = new ArrayList<>();
        for (int initPageNum = 1; ; initPageNum++) {
            apiIn.setPageNum(initPageNum);
            final Result<TableDataInfo<ApiOut>> result = api.apply(apiIn);
            final List<ApiOut> searchList = ofNullable(result.getBody()).map(TableDataInfo::getContent).orElseGet(ArrayList::new);
            if (CollUtil.isEmpty(searchList)) {
                break;
                //如果查出来有东西,但是不足限制大小,加进去
            } else if (searchList.size() < limitSize) {
                ret.addAll(searchList);
                break;
            }
            //走到这说明等于限制大小,下页必有数据,加进去接着循环
            ret.addAll(searchList);
        }
        return ret;
    }

    /**
     * 分页api割接得到所有列表
     *
     * @param limitSize 限制大小
     * @param inBO      入参业务BO
     * @param convert   转换
     * @param api       api
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/06
     */
    public static <In, ApiOut, ApiIn extends PageCondition> List<ApiOut> getAllListFromPageRes( final int limitSize,
                                                                                               In inBO,
                                                                                               Function<In, ApiIn> convert,
                                                                                               Function<ApiIn, Result<PageResult<ApiOut>>> api) {
        final ApiIn apiIn = convert.apply(inBO);
        return getAllListFromPageRes(limitSize, apiIn, api);
    }

    public static <In, ApiOut, ApiIn extends PageCondition> List<ApiOut> getAllListFromPageRes( final int limitSize,
                                                                                               ApiIn apiIn,
                                                                                               Function<ApiIn, Result<PageResult<ApiOut>>> api) {
        List<ApiOut> ret = new ArrayList<>();
        apiIn.setPageNum(1);
        apiIn.setPageSize(limitSize);
        final PageResult<ApiOut> body = api.apply(apiIn).getBody();
        final Integer total = ofNullable(body).map(PageResult::getTotal).orElse(null);
        //如果对方返回了total,走下面方法节省算力
        if (Objects.nonNull(total)) {return getAllListFromPageResWithTotal(limitSize, apiIn, api);}

        for (int initPageNum = 1; ; initPageNum++) {
            apiIn.setPageNum(initPageNum);
            final Result<PageResult<ApiOut>> result = api.apply(apiIn);
            final List<ApiOut> searchList = ofNullable(result.getBody()).map(PageResult::getData).orElseGet(ArrayList::new);
            if (CollUtil.isEmpty(searchList)) {
                break;
                //如果查出来有东西,但是不足限制大小,加进去
            } else if (searchList.size() < limitSize) {
                ret.addAll(searchList);
                break;
            }
            //走到这说明等于限制大小,下页必有数据,加进去接着循环
            ret.addAll(searchList);
        }
        return ret;
    }

    public static <In, ApiOut, ApiIn extends PageCondition> List<ApiOut> getAllListFromPageResWithTotal( final int limitSize,
                                                                                                        ApiIn apiIn,
                                                                                                        Function<ApiIn, Result<PageResult<ApiOut>>> api) {
        int initPageNum = 1;
        apiIn.setPageNum(initPageNum);
        apiIn.setPageSize(limitSize);
        //先查一次
        final Result<PageResult<ApiOut>> result = api.apply(apiIn);
        final PageResult<ApiOut> body = result.getBody();
        final int total = ofNullable(body).map(PageResult::getTotal).orElse(0);
        List<ApiOut> ret = new ArrayList<>(ofNullable(body).map(PageResult::getData).orElseGet(Collections::emptyList));
        if (total < limitSize) {return ret;}
        //条数除以限制向上取整(即调用分页的次数)
        final double ceil = Math.ceil((double) total / limitSize);

        for (int nextPageNum = 2; nextPageNum <= ceil; nextPageNum++) {
            apiIn.setPageNum(nextPageNum);
            final PageResult<ApiOut> bodynext = api.apply(apiIn).getBody();
            final List<ApiOut> data = ofNullable(bodynext.getData()).orElseGet(Collections::emptyList);
            ret.addAll(data);
        }
        ret.removeIf(Objects::isNull);
        return ret;
    }

    /**
     * 调用时一行代码解决分页问题->AllList转TableDataInfo
     * 外部接口返回的如果如果做了处理,这里的OutList泛型和Out不是同一个泛型BO
     * 外部接口返回的如果如果没做处理,这里的OutList泛是同一个泛型BO,那就不传mapstrucConvert,调用重载方法即可
     *
     * @param in               查询条件bffvo
     * @param list             agg查出来的list
     * @param mapstructConvert mapstruct转换aggOut转bffOut : TableDataInfo<BffOutVo> aggToBff(TableDataInfo<OutList> tabAggOutBO);
     * @param <IN>             bff入参vo泛型
     * @param <Out>            bff出参vo泛型
     * @param <OutList>        agg出参bo泛型
     * @return 返回信息
     * @author zjarlin
     */
    public static <IN extends PageCondition, Out, OutList> TableDataInfo<Out> getTableDataInfo( IN in,  List<OutList> list, final  Function<TableDataInfo<OutList>, TableDataInfo<Out>> mapstructConvert) {
        final int total = list.size();
        final TableDataInfo<OutList> tab = new TableDataInfo<>(list, total);
        final int pageNum = Math.max(1, in.getPageNum());
        final int pageSize = in.getPageSize();
        if (pageSize == 0) {return TableDataInfo.EMPTY;}
        tab.setPageNum(pageNum);
        tab.setPageSize(pageSize);
        final List<OutList> collect = list.stream().skip((long) (pageNum - 1) * tab.getPageSize()).limit(pageSize).collect(Collectors.toList());
        final int totalPage = (total + pageSize - 1) / pageSize;
        tab.setTotalPage(totalPage);
        tab.setContent(collect);
        return Optional.of(tab).map(mapstructConvert).orElseGet(TableDataInfo::new);
    }

    /**
     * 外部接口返回的如果如果做了处理,这里的OutList泛型做了处理和Out不是同一个泛型BO
     *
     * @param in               入参
     * @param list             列表
     * @param mapstructConvert mapstruct转换
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    public static <IN extends PageCondition, Out, OutList> Result<TableDataInfo<Out>> getTableDataInfoResult( IN in,  List<OutList> list,
                                                                                                             final  Function<TableDataInfo<OutList>, TableDataInfo<Out>> mapstructConvert) {
        return Result.build(getTableDataInfo(in, list, mapstructConvert));
    }

    /**
     * 外部接口返回的如果如果没做处理,这里的OutList泛是同一个泛型BO,无需传递mapstructConvert方法引用
     *
     * @param in   入参
     * @param list 列表
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    public static <IN extends PageCondition, Out> Result<TableDataInfo<Out>> getTableDataInfoResult( IN in,  List<Out> list) {
        return Result.build(getTableDataInfo(in, list));
    }

    /**
     * @param in    查询条件bffvo
     * @param list  agg查出来的list
     * @param <IN>  入参泛型
     * @param <Out> 出参泛型
     * @return 返回分页信息
     */
    public static <IN extends PageCondition, Out> TableDataInfo<Out> getTableDataInfo( IN in,  List<Out> list) {
        final int total = list.size();
        final TableDataInfo<Out> tab = new TableDataInfo<>(list, total);
        final int pageNum = Math.max(1, in.getPageNum());
        final int pageSize = in.getPageSize();
        if (pageSize == 0) {return TableDataInfo.EMPTY;}

        tab.setPageNum(pageNum);
        tab.setPageSize(pageSize);
        final List<Out> collect = list.stream().skip((long) (pageNum - 1) * tab.getPageSize()).limit(pageSize).collect(Collectors.toList());
        final int totalPage = (total + pageSize - 1) / pageSize;
        tab.setTotalPage(totalPage);
        tab.setContent(collect);
        final TableDataInfo<Out> target = new TableDataInfo<>();
        BeanUtils.copyProperties(tab, target);
        return Optional.of(target).orElseGet(TableDataInfo::new);
    }
//////////////////////////////////////////////以下是返回PageResult(没有当前页码属性所以弃用)而非TableDataInfo

    /**
     * 入参转分页
     *
     * @param in               入参
     * @param list             列表
     * @param mapstructConvert mapstruct转换
     * @return 返回信息
     * @author zjarlin
     * @since 2022/06/29
     */
    public static <IN extends PageCondition, Out, OutList> PageResult<Out> getPageResult( IN in,  List<OutList> list,
                                                                                         final  Function<PageResult<OutList>, PageResult<Out>> mapstructConvert) {
        final int total = list.size();
        final int pageNum = in.getPageNum();
        final int pageSize = in.getPageSize();
        final int page = (total + pageSize - 1) / pageSize;
        final List<OutList> data = list.stream().skip((long) (Math.max(1, pageNum) - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
        final PageResult<OutList> tab = new PageResult<>(data, page, pageSize, total);
        return Optional.of(tab).map(mapstructConvert).orElseGet(PageResult::new);
    }

    /**
     * list转分页
     *
     * @param in   入参
     * @param list 列表
     * @return 返回分页信息
     * @author zjarlin
     * @since 2022/06/29
     */
    public static <IN extends
            PageCondition, Out, OutList> PageResult<Out> getPageResult( IN in,  List<OutList> list) {
        final int total = list.size();
        final int pageNum = in.getPageNum();
        final int pageSize = in.getPageSize();
        final int page = (total + pageSize - 1) / pageSize;
        final List<OutList> data = list.stream().skip((long) (Math.max(1, pageNum) - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
        final PageResult<OutList> tab = new PageResult<>(data, page, pageSize, total);
        final PageResult<Out> target = new PageResult<>();
        BeanUtils.copyProperties(tab, target);
        return Optional.of(target).orElseGet(PageResult::new);
    }

}
